package com.harmontronics.hanma.paas.paas.domain.app.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.harmontronics.hanma.paas.paas.common.PageBase;
import com.harmontronics.hanma.paas.paas.consts.DbConstants;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmEntityModel;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.*;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmEntityModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmEntityModelPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmModelPO;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ldb
 */
@Service("hmEntityModelService")
@DS("#header.App-Unique")
public class HmEntityModelService {

    @Autowired
    private HmEntityModelRepository hmEntityModelRepository;

    @Autowired
    private HmModelRepository hmModelRepository;

    @Autowired
    private HmPublishedEntityModelService hmPublishedEntityModelService;


    public HmEntityModel findById(Long id) {
        HmEntityModelPO po = hmEntityModelRepository.getById(id);
        return BeanUtil.toBean(po, HmEntityModel.class);
    }


    public void saveDefaultEntityModel(Long modelId, Boolean supportProcess, Boolean supportTree,Boolean isEntityModel) {
        createBuiltInColumn(DbConstants.ID, "ID", 0, 0,
                EntityModelTypeEnum.NUMBER, modelId);
        if(isEntityModel){
            createBuiltInColumn(DbConstants.CREATE_BY, "创建人", 0, 100,
                    EntityModelTypeEnum.SHORT_TEXT, modelId);
            createBuiltInColumn(DbConstants.CREATE_DATE, "创建时间", 0, 0,
                    EntityModelTypeEnum.DATE, modelId);
            createBuiltInColumn(DbConstants.MODIFY_BY, "修改人", 1, 100,
                    EntityModelTypeEnum.SHORT_TEXT, modelId);
            createBuiltInColumn(DbConstants.MODIFY_DATE, "修改时间", 1, 0,
                    EntityModelTypeEnum.DATE, modelId);
            createBuiltInColumn(DbConstants.DELETED, "是否已删除", 1, 0,
                    EntityModelTypeEnum.NUMBER, modelId);
        }

        if (isEntityModel && supportProcess != null && supportProcess) {
            createBuiltInColumn(Fields.PROCESS_ID, "流程ID", 1, 0,
                    EntityModelTypeEnum.NUMBER, modelId);
            createBuiltInColumn(Fields.PROCESS_INSTANCE_ID, "流程实例ID", 1, 255,
                    EntityModelTypeEnum.SHORT_TEXT, modelId);
        }
        if (supportTree != null && supportTree) {
            // 实体模型中，parent_id使用long类型，并非 EntityModelTypeEnum.PARENT_ID 规定的字符串类型
            createBuiltInColumn(Fields.PARENT_ID, "父节点ID", 1, 0,
                    EntityModelTypeEnum.PARENT_ID, modelId);
            createBuiltInColumn(Fields.PARENT_IDS, "所有父级ID", 1, 255,
                    EntityModelTypeEnum.SHORT_TEXT, modelId);
            createBuiltInColumn(Fields.CHILDREN, "子节点", 1, 255,
                    EntityModelTypeEnum.CHILDREN, modelId);
            createBuiltInColumn(Fields.NODE_NAME, "节点名称", 1, 100,
                    EntityModelTypeEnum.SHORT_TEXT, modelId);
        }
    }


    public void createBuiltInColumn(String code,String name,Integer nullable,Integer length,EntityModelTypeEnum type,Long modelId){
        HmEntityModelPO entityModelPO = new HmEntityModelPO();
        entityModelPO.setCode(code);
        entityModelPO.setName(name);
        entityModelPO.setNullable(nullable);
        entityModelPO.setLength(length);
        entityModelPO.setType(type);
        entityModelPO.setModelId(modelId);
        entityModelPO.setStatus(EntityModelStatusEnum.PUBLISHED);
        entityModelPO.setBuiltIn(Boolean.TRUE);
        // 默认节点显示规则使用 node_name，完整配置为: ${node_name}
        if(code.equals(Fields.PARENT_ID)){
            JSONObject config = new JSONObject();
            config.set("value", String.format("${%s}", Fields.NODE_NAME));
            entityModelPO.setConfig(config.toString());
        }
        hmEntityModelRepository.save(entityModelPO);
    }

    @Transactional(rollbackFor = Exception.class)
    public String save(HmEntityModel hmEntityModel) {
        // TODO - tylr 2021/6/1 19:46 检查字段关联业务模型下 config 配置是否正确
        //  校验规则：1）根据 relationType 和 type 校验 config；2）config中的字段不能为单选关联、
        //  多选关联和子表类型的字段
        uniqueCreate(hmEntityModel);
        HmEntityModelPO po = BeanUtil.toBean(hmEntityModel, HmEntityModelPO.class);
        fillProperties(po);
        po.setStatus(EntityModelStatusEnum.SCRIPT);
        hmEntityModelRepository.save(po);
        HmModelPO modelPO = hmModelRepository.getById(hmEntityModel.getModelId());
        if (modelPO.getType() == ModelTypeEnum.ENTITY) {
            return addColumn2Db(po);
        }
        return null;
    }


    private String addColumn2Db(HmEntityModelPO po) {
        HmModelPO modelPo = hmModelRepository.getById(po.getModelId());
        String columnDef = po.getType().getColumnDef(po.getCode(), po.getLength(), po.getNullable() == 1);
        try {
            return hmEntityModelRepository.addColumn(modelPo.getCode(), columnDef);
        } catch (Exception ex) {
            String message = ex.getMessage();
            if (message != null && message.contains("syntax")) {
                throw new ErrorKeyException(I18nKeyEnum.DB_KEY_WORD_ERROR);
            } else {
                throw ex;
            }
        }
    }

    public void uniqueCreate(HmEntityModel model) {
        if (hmEntityModelRepository.findByCodeAndModelId(model.getCode(), model.getModelId()) != null) {
            throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_CODE_EXISTS);
        }
        if (hmEntityModelRepository.findByNameAndModelId(model.getName(), model.getModelId()) != null) {
            throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_NAME_EXISTS);
        }
    }

    private void fillProperties(HmEntityModelPO po) {
        if (po.getLength() == null) {
            po.setLength(0);
        }
        if (po.getNullable() == null) {
            po.setNullable(0);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String update(HmEntityModel hmEntityModel) {
        HmEntityModelPO dbPo = hmEntityModelRepository.getById(hmEntityModel.getId());
        uniqueUpdate(hmEntityModel);
        HmModelPO hmModelPO = hmModelRepository.getById(dbPo.getModelId());
        //修改字段
        //去除非空约束
//        if (hmEntityModel.getNullable() == 1 && dbPo.getNullable() == 0) {
//            try {
//                hmEntityModelRepository.dropNotNull(hmModelPO.getCode(), hmEntityModel.getCode());
//            } catch (Exception ex) {
//                ex.printStackTrace();
//                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODE_UPDATE_DB_ERROR);
//            }
//        }

        //添加非空约束
//        if (hmEntityModel.getNullable() == 0 && dbPo.getNullable() == 1) {
//            try {
//                hmEntityModelRepository.setNotNull(hmModelPO.getCode(), hmEntityModel.getCode());
//            } catch (Exception ex) {
//                ex.printStackTrace();
//                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODE_UPDATE_DB_ERROR);
//            }
//        }

        //只能修改 名称 长度 和 是否可为空
        dbPo.setName(hmEntityModel.getName());
        dbPo.setLength(hmEntityModel.getLength());
        dbPo.setNullable(hmEntityModel.getNullable());
        hmEntityModelRepository.updateById(dbPo);
        if (hmModelPO.getType() == ModelTypeEnum.ENTITY) {
            //字符串长度修改
            if (dbPo.getType().isVarCharType() && hmEntityModel.getLength() != null && hmEntityModel.getLength().intValue() != dbPo.getLength().intValue()) {
                try {
                    return hmEntityModelRepository.setVarLength(hmModelPO.getCode(), dbPo.getCode(), hmEntityModel.getLength());
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODE_UPDATE_DB_ERROR);
                }
            }
        }
        return null;
    }

    public void uniqueUpdate(HmEntityModel model) {
        HmEntityModelPO po = hmEntityModelRepository.findByNameAndModelId(model.getName(), model.getModelId());
        if (po == null) {
            return;
        }
        if (po.getId().longValue() != model.getId().longValue()) {
            throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_NAME_EXISTS);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public List<String> remove(List<Long> ids) {
        HmModelPO hmModelPO = null;
        List<String> sqls = new ArrayList<>();
        for (Long id : ids) {
            HmEntityModelPO hmEntityModelPO = hmEntityModelRepository.getById(id);
            if (hmEntityModelPO == null) {
                continue;
            }
            hmModelPO = hmModelRepository.getById(hmEntityModelPO.getModelId());
            if (hmModelPO.getType() == ModelTypeEnum.ENTITY) {
                String sql = hmEntityModelRepository.dropColumn(hmModelPO.getCode(), hmEntityModelPO.getCode());
                sqls.add(sql);
            }
            hmEntityModelRepository.removeById(id);
        }
        return sqls;

    }


    public List<HmEntityModel> getList(Long modelId,String modelKey,Boolean activated) {
        //获取激活版本的字段数据
        if (activated != null && activated) {
            if(modelId!=null) {
                return hmPublishedEntityModelService.getListByModelId(modelId);
            }else{
                HmModelPO modelPO = hmModelRepository.findByCode(modelKey);
                return hmPublishedEntityModelService.getListByModelId(modelPO.getId());
            }
        }

        //获取模型所有的字段
        QueryWrapper<HmEntityModelPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc(DbConstants.CREATE_DATE);
        if (modelId != null) {
            queryWrapper.eq(Fields.MODEL_ID, modelId);
        } else{
            HmModelPO modelPO = hmModelRepository.findByCode(modelKey);
            queryWrapper.eq(Fields.MODEL_ID, modelPO.getId());
        }
        List<HmEntityModelPO> pos = hmEntityModelRepository.list(queryWrapper);
        return Convert.toList(HmEntityModel.class, pos);
    }


    public PageBase<HmEntityModel> getList(Long modelId, boolean hiddenSystemField, int pageNo, int pageSize) {
        Page<HmEntityModelPO> page = new Page<>(pageNo, pageSize);
        QueryWrapper<HmEntityModelPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Fields.MODEL_ID, modelId);
        queryWrapper.eq(hiddenSystemField,Fields.BUILT_IN, Boolean.FALSE);
        queryWrapper.orderByDesc(DbConstants.CREATE_DATE);
        IPage<HmEntityModelPO> resultPage =
                hmEntityModelRepository.selectPage(queryWrapper, page);
        return PageBase.from(resultPage, HmEntityModel.class);
    }


    public void validField(Long id, String field, String value, Long modelId) {
        if (Fields.NAME.equals(field)) {
            HmEntityModelPO po = hmEntityModelRepository.findByNameAndModelId(value, modelId);
            if (po == null) {
                return;
            }
            if (id == null) {
                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_NAME_EXISTS);
            }
            if (po.getId().longValue() != id.longValue()) {
                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_NAME_EXISTS);
            }
        }

        if (Fields.CODE.equals(field)) {
            HmEntityModelPO po = hmEntityModelRepository.findByCodeAndModelId(value, modelId);
            if (po == null) {
                return;
            }
            if (id == null) {
                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_CODE_EXISTS);
            }
            if (po.getId().longValue() != id.longValue()) {
                throw new ErrorKeyException(I18nKeyEnum.ENTITY_MODEL_CODE_EXISTS);
            }
        }
    }


}